package com.tsfyun.scm.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.mail.UserPassAuthenticator;
import com.sun.mail.util.MailSSLSocketFactory;
import com.tsfyun.common.base.exception.ServiceException;
import com.tsfyun.common.base.util.StringUtils;
import com.tsfyun.common.base.util.TsfPreconditions;
import com.tsfyun.common.base.util.TypeUtils;
import lombok.extern.slf4j.Slf4j;

import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.mail.*;
import javax.mail.Message.RecipientType;
import javax.mail.internet.*;
import java.io.File;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 邮件发送工具类，由于spring-boot-starter-mail无法动态根据租户配置，改由普通代码实现
 */
@Slf4j
public class MailUtil {

    /**
     * 内部类单例模式
     */
    private static class MailUtilInstance{
        private static final MailUtil instance = new MailUtil();
    }

     private MailUtil(){}

     public static MailUtil getInstance(){
        return MailUtilInstance.instance;
    }

    /**
     * 存放租户邮箱配置，首次调用的的时候需要赋值（线程安全可见）
     * mail.host      发送邮件服务器host
     * mail.user  发送邮件服务器用户名
     * mail.password  发送邮件服务器用密码
     * mail.retryTimes 重试次数
     */
    public volatile ConcurrentHashMap<String, Properties> mailMap = new ConcurrentHashMap<>();


    /**
     * 发给多个收件人
     * @param receivers
     * @param title
     * @param content
     * @param properties
     * @throws Exception
     */
    private void sendByPro(List<String> receivers, String title, String content, Properties properties) throws Exception{
            properties.put("mail.smtp.auth", "true"); //这样才能通过验证

            Integer mailPort = Integer.parseInt(properties.getProperty("mail.port"));
            //开启安全协议
            if(465 == mailPort || 993 == mailPort) {
                MailSSLSocketFactory sf;
                try {
                    sf = new MailSSLSocketFactory();
                    sf.setTrustAllHosts(true);
                } catch (GeneralSecurityException e) {
                    throw new ServiceException("测试失败");
                }
                properties.put("mail.smtp.ssl.enable", "true");
                properties.put("mail.smtp.ssl.socketFactory", sf);
            }

            //获得对话 session
            Session session = Session.getDefaultInstance(properties);
            session.setDebug(false);

            // 设置 message
            MimeMessage message = new MimeMessage(session);
            message.setDataHandler(new DataHandler(content, "text/html; charset=UTF-8"));
            //设置发件人(多个)
            message.setFrom(new InternetAddress(properties.getProperty("mail.user")));
            if (CollUtil.isNotEmpty(receivers)) {
                Address[] addresses = new InternetAddress[receivers.size()];
                for (int i = 0; i < receivers.size(); i++) {
                    addresses[i] = new InternetAddress((StringUtils.null2EmptyWithTrim(receivers.get(i))));
                }
                message.addRecipients(RecipientType.TO, addresses);
            }
            //设置邮件主题
            message.setSubject(title);
            Multipart multipart = new MimeMultipart();
            MimeBodyPart messagePart = new MimeBodyPart();
            //设置邮件文本内容
            messagePart.setContent(content, "text/html; charset=UTF-8");
            multipart.addBodyPart(messagePart);
            message.setContent(multipart);
            message.setSentDate(new Date());
            //保存message
            message.saveChanges();
            //获得发送端口
            Transport transport = session.getTransport("smtp");
            transport.connect(properties.getProperty("mail.host"), Integer.parseInt(properties.getProperty("mail.port")),properties.getProperty("mail.user"),
                    properties.getProperty("mail.password"));
            //发送邮件
            transport.sendMessage(message, message.getAllRecipients());
            transport.close();
        }


    /**
     * 发给多个收件人
     * @param receivers
     * @param title
     * @param content
     * @throws Exception
     */
    public String sendSimple(List<String> receivers, String title, String content) {
        //根据租户获取邮件服务器配置信息
        String errmsg = null;
        Properties properties = mailMap.get("scm");
        if(Objects.isNull(properties)) {
            return "未读取到邮件服务器配置";
        }
        //如果未配置，默认重试三次
        int retryTimes = TypeUtils.castToInt(properties.getProperty("mail.retryTimes"),3);
        boolean isSendSuccess = false;
        for(int i = 0;i < retryTimes;i++) {
            try{
                if(isSendSuccess) {
                    break;
                }
                log.info(String.format("第%d次发送邮件准备",i + 1));
                sendByPro(receivers,title,content,properties);
                isSendSuccess = true;
                errmsg = null;
                log.info(String.format("第%d次发送邮件成功",i + 1));
            } catch (Exception e) {
                log.error(String.format("第%d次发送邮件异常",i + 1),e);
                errmsg = e.getMessage();
            }
        }
        return errmsg;
    }


    /**
     * 发给多个收件人，带附件
     * @param receivers
     * @param title
     * @param content
     * @throws Exception
     */
    public String sendAttach(List<String> receivers, String title, String content,File... attaches) {
        //根据租户获取邮件服务器配置信息
        String errmsg = null;
        Properties properties = mailMap.get("scm");
        if(Objects.isNull(properties)) {
            return "未读取到邮件服务器配置";
        }
        TsfPreconditions.checkArgument(Objects.nonNull(properties),new ServiceException("未读取到邮件服务器配置"));
        //如果未配置，默认重试三次
        int retryTimes = TypeUtils.castToInt(properties.getProperty("mail.retryTimes"),3);
        boolean isSendSuccess = false;
        for(int i = 0;i < retryTimes;i++) {
            try{
                if(isSendSuccess) {
                    break;
                }
                log.info(String.format("第%d次发送带附件邮件准备",i + 1));
                sendAttach(receivers,title,content,properties,attaches);
                isSendSuccess = true;
                errmsg = null;
                log.info(String.format("第%d次发送带附件邮件成功",i + 1));
            } catch (Exception e) {
                log.error(String.format("第%d次发送带附件邮件异常",i + 1),e);
                errmsg = e.getMessage();
            }
        }
        return errmsg;
    }

    /**
     * 带附件的邮件
     * @param receivers
     * @param title
     * @param content
     * @param properties
     * @param attaches
     * @throws Exception
     */
    private void sendAttach (List<String> receivers, String title, String content, Properties properties,File... attaches) throws Exception {
        properties.put("mail.smtp.auth", "true"); //这样才能通过验证
        Integer mailPort = Integer.parseInt(properties.getProperty("mail.port"));
        //开启安全协议
        if(465 == mailPort || 993 == mailPort) {
            MailSSLSocketFactory sf;
            try {
                sf = new MailSSLSocketFactory();
                sf.setTrustAllHosts(true);
            } catch (GeneralSecurityException e) {
                throw new ServiceException("测试失败");
            }
            properties.put("mail.smtp.ssl.enable", "true");
            properties.put("mail.smtp.ssl.socketFactory", sf);
        }
        //获得对话 session
        Session session = Session.getDefaultInstance(properties);
        session.setDebug(false);
        // 设置 message
        MimeMessage message = new MimeMessage(session);
        message.setDataHandler(new DataHandler(content,
                    "text/html; charset=UTF-8"));
        //设置发件人
        message.setFrom(new InternetAddress(properties.getProperty("mail.user")));
        //设置收件人
        if (CollUtil.isNotEmpty(receivers)) {
            Address[] addresses = new InternetAddress[receivers.size()];
            for (int i = 0; i < receivers.size(); i++) {
                addresses[i] = new InternetAddress((StringUtils.null2EmptyWithTrim(receivers.get(i))));
            }
            message.addRecipients(RecipientType.TO, addresses);
        }
        //设置邮件主题
        message.setSubject(title);
        Multipart multipart = new MimeMultipart();
        MimeBodyPart messagePart = new MimeBodyPart();
        //设置邮件文本内容
        messagePart.setContent(content, "text/html; charset=UTF-8");
        multipart.addBodyPart(messagePart);
        //设置邮件附件，可以有多个
        if (null != attaches && attaches.length > 0) {
            for (int i = 0; i < attaches.length; i++) {
                //防止文件不存在
                if(attaches[i].exists()) {
                    messagePart = new MimeBodyPart();
                    //得到数据源
                    FileDataSource fds = new FileDataSource(attaches[i]);
                    //得到附件本身并至入BodyPart
                    messagePart.setDataHandler(new DataHandler(fds));
                    //得到文件名同样至入BodyPart，文件名需编码，否则中文乱码
                    messagePart.setFileName(MimeUtility.encodeWord(attaches[i].getName()));
                    multipart.addBodyPart(messagePart);
                } else {
                    log.error(String.format("文件%s不存在",attaches[i].getName()));
                }
            }
        }
        message.setContent(multipart);
        message.setSentDate(new Date());

        // 保存message
        message.saveChanges();

        //获得发送端口
        Transport transport = session.getTransport("smtp");
        transport.connect(properties.getProperty("mail.host"), Integer.parseInt(properties.getProperty("mail.port")),properties.getProperty("mail.user"),
                properties.getProperty("mail.password"));
        //发送邮件
        transport.sendMessage(message, message.getAllRecipients());
        transport.close();
    }

    /**
     * 测试连接
     * @param host
     * @param username
     * @param password
     * @return
     */
    public Boolean checkSend(String host,String username,String password,Integer mailPort) {
        Properties prop = new Properties();
        prop.setProperty("mail.smtp.auth", "true"); //这样才能通过验证
        //设置超时时间为5秒
        prop.setProperty("mail.smtp.timeout", "5000");
        prop.setProperty("mail.transport.protocol", "smtp");
        prop.setProperty("mail.smtp.port", mailPort.toString());
        prop.setProperty("mail.smtp.host", host);

        //开启安全协议
        if(465 == mailPort || 993 == mailPort) {
            MailSSLSocketFactory sf;
            try {
                sf = new MailSSLSocketFactory();
                sf.setTrustAllHosts(true);
            } catch (GeneralSecurityException e) {
                throw new ServiceException("测试失败");
            }
            prop.put("mail.smtp.ssl.enable", "true");
            prop.put("mail.smtp.ssl.socketFactory", sf);
        }

        boolean result;
        //1、创建session
        Session session = Session.getInstance(prop);
        //开启Session的debug模式，这样就可以查看到程序发送Email的运行状态
        //session.setDebug(true);
        //2、通过session得到transport对象
        Transport ts;
        //3、使用邮箱的用户名和密码连上邮件服务器，发送邮件时，发件人需要提交邮箱的用户名和密码给smtp服务器，用户名和密码都通过验证之后才能够正常发送邮件给收件人。
        try {
            ts = session.getTransport();
            ts.connect(host, username, password);
            ts.close();
            result = Boolean.TRUE;
        } catch (NoSuchProviderException e1) {
            log.error(StrUtil.format("测试邮件连接异常，邮箱服务器:{},邮箱用户名:{}",host,username),e1);
            result = Boolean.FALSE;
        } catch (MessagingException e) {
            log.error(StrUtil.format("测试邮件连接异常，邮箱服务器:{},邮箱用户名:{}",host,username),e);
            result = Boolean.FALSE;
        }
        return result;
    }



}

